all repos — caroster @ 54cb6c4b8a7dd7edbecc198ae921331a52d5aa90

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Tooltip from '@mui/material/Tooltip';
  3import IconButton from '@mui/material/IconButton';
  4import Box from '@mui/material/Box';
  5import Link from '@mui/material/Link';
  6import Card from '@mui/material/Card';
  7import Container from '@mui/material/Container';
  8import TextField from '@mui/material/TextField';
  9import Typography from '@mui/material/Typography';
 10import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';
 11import EventIcon from '@mui/icons-material/Event';
 12import TuneIcon from '@mui/icons-material/Tune';
 13import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 14import {useTheme} from '@mui/material/styles';
 15import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 16import {PropsWithChildren, useState} from 'react';
 17import {useTranslation} from 'react-i18next';
 18import pageUtils from '../../../lib/pageUtils';
 19import ShareEvent from '../../../containers/ShareEvent';
 20import useEventStore from '../../../stores/useEventStore';
 21import useToastStore from '../../../stores/useToastStore';
 22import useMapStore from '../../../stores/useMapStore';
 23import Map from '../../../containers/Map';
 24import EventLayout, {TabComponent} from '../../../layouts/Event';
 25import {
 26  EventByUuidDocument,
 27  useUpdateEventMutation,
 28} from '../../../generated/graphql';
 29import EventPopup from '../../../containers/EventPopup';
 30import AddressAutofill from '../../../containers/AddressAutofill';
 31
 32interface Props {
 33  eventUUID: string;
 34  announcement?: string;
 35}
 36
 37const Page = (props: PropsWithChildren<Props>) => {
 38  return <EventLayout {...props} Tab={DetailsTab} />;
 39};
 40
 41const DetailsTab: TabComponent = ({}) => {
 42  const {t} = useTranslation();
 43  const theme = useTheme();
 44  const {preventUpdateKey, setPreventUpdateKey, setCenter, setMarkers} =
 45    useMapStore();
 46  const [updateEvent] = useUpdateEventMutation();
 47  const addToast = useToastStore(s => s.addToast);
 48  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 49  const event = useEventStore(s => s.event);
 50  const [isEditing, setIsEditing] = useState(false);
 51
 52  const onSave = async e => {
 53    try {
 54      const {uuid, ...data} = event;
 55      const {id, travels, waitingPassengers, __typename, ...input} = data;
 56      await updateEvent({
 57        variables: {
 58          uuid,
 59          eventUpdate: {
 60            ...input,
 61          },
 62        },
 63        refetchQueries: ['eventByUUID'],
 64      });
 65      setIsEditing(false);
 66    } catch (error) {
 67      console.error(error);
 68      addToast(t('event.errors.cant_update'));
 69    }
 70  };
 71
 72  const modifyButton = isEditing ? (
 73    <Tooltip
 74      title={t('event.details.save')}
 75      sx={{
 76        position: 'absolute',
 77        top: theme.spacing(2),
 78        right: theme.spacing(2),
 79      }}
 80    >
 81      <IconButton color="primary" onClick={onSave}>
 82        <CheckCircleOutlineIcon />
 83      </IconButton>
 84    </Tooltip>
 85  ) : (
 86    <Tooltip
 87      title={t('event.details.modify')}
 88      sx={{
 89        position: 'absolute',
 90        top: theme.spacing(2),
 91        right: theme.spacing(2),
 92      }}
 93    >
 94      <IconButton color="primary" onClick={() => setIsEditing(true)}>
 95        <TuneIcon />
 96      </IconButton>
 97    </Tooltip>
 98  );
 99
100  if (!event) return null;
101  const {latitude, longitude} = event;
102
103  const mapUpdateKey = `${event.uuid}.details`;
104  if (preventUpdateKey !== mapUpdateKey) {
105    setPreventUpdateKey(mapUpdateKey);
106    setCenter([latitude, longitude]);
107    setMarkers([
108      {
109        double: true,
110        center: [latitude, longitude],
111        popup: <EventPopup event={event} />,
112      },
113    ]);
114  }
115
116  return (
117    <Box
118      sx={{
119        position: 'relative',
120      }}
121    >
122      {latitude && longitude ? <Map /> : <Box pt={6} />}
123      <Container
124        sx={{
125          p: 4,
126          mb: 11,
127          mx: 0,
128          [theme.breakpoints.down('md')]: {
129            p: 2,
130          },
131        }}
132      >
133        <Card
134          sx={{
135            position: 'relative',
136            maxWidth: '100%',
137            width: '350px',
138            p: 2,
139          }}
140        >
141          <Typography variant="h4" pb={2}>
142            {t('event.details')}
143          </Typography>
144          {modifyButton}
145          <Box pt={2} pr={1.5}>
146            <Typography variant="overline">{t('event.fields.name')}</Typography>
147            <Typography variant="body1">
148              {isEditing ? (
149                <TextField
150                  size="small"
151                  fullWidth
152                  value={event.name}
153                  onChange={e => setEventUpdate({name: e.target.value})}
154                  name="name"
155                  id="EditEventName"
156                />
157              ) : (
158                <Typography variant="body1" id="EventName">
159                  {event.name ?? t('event.fields.empty')}
160                </Typography>
161              )}
162            </Typography>
163          </Box>
164          <Box pt={2} pr={1.5}>
165            <Typography variant="overline">{t('event.fields.date')}</Typography>
166            {isEditing ? (
167              <Typography variant="body1">
168                <DatePicker
169                  slotProps={{
170                    textField: {
171                      size: 'small',
172                      id: `EditEventDate`,
173                      fullWidth: true,
174                      placeholder: t('event.fields.date_placeholder'),
175                    },
176                  }}
177                  format="DD/MM/YYYY"
178                  value={moment(event.date)}
179                  onChange={date =>
180                    setEventUpdate({
181                      date: !date ? null : moment(date).format('YYYY-MM-DD'),
182                    })
183                  }
184                />
185              </Typography>
186            ) : (
187              <Box position="relative">
188                <Typography variant="body1" id="EventDate">
189                  {event.date
190                    ? moment(event.date).format('DD/MM/YYYY')
191                    : t('event.fields.empty')}
192                </Typography>
193                <EventIcon
194                  color="action"
195                  sx={{
196                    position: 'absolute',
197                    right: theme.spacing(-0.5),
198                    top: 0,
199                  }}
200                />
201              </Box>
202            )}
203          </Box>
204          <Box pt={2} pr={1.5}>
205            <Typography variant="overline">
206              {t('event.fields.address')}
207            </Typography>
208            {isEditing ? (
209              <AddressAutofill
210                label={t('event.creation.address')}
211                address={event.address}
212                onSelect={({location, address}) => {
213                  setEventUpdate({
214                    address,
215                    latitude: location[1],
216                    longitude: location[0],
217                  });
218                }}
219              />
220            ) : (
221              <Box position="relative">
222                <Typography variant="body1" id="EventAddress" sx={{pr: 3}}>
223                  {event.address ? (
224                    <Link
225                      target="_blank"
226                      rel="noreferrer"
227                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
228                        event.address
229                      )}`}
230                      onClick={e => e.preventDefault}
231                    >
232                      {event.address}
233                    </Link>
234                  ) : (
235                    t('event.fields.empty')
236                  )}
237                </Typography>
238                <PlaceOutlinedIcon
239                  color="action"
240                  sx={{
241                    position: 'absolute',
242                    right: theme.spacing(-0.5),
243                    top: 0,
244                  }}
245                />
246              </Box>
247            )}
248          </Box>
249          <Box pt={2} pr={1.5}>
250            <Typography variant="overline">
251              {t('event.fields.description')}
252            </Typography>
253            {isEditing ? (
254              <Typography variant="body1">
255                <TextField
256                  fullWidth
257                  multiline
258                  maxRows={4}
259                  inputProps={{maxLength: 250}}
260                  value={event.description || ''}
261                  onChange={e => setEventUpdate({description: e.target.value})}
262                  id={`EditEventDescription`}
263                  name="description"
264                />
265              </Typography>
266            ) : (
267              <Typography variant="body1" id="EventDescription" sx={{pr: 3}}>
268                {event.description ?? t('event.fields.empty')}
269              </Typography>
270            )}
271          </Box>
272          {!isEditing && (
273            <ShareEvent
274              title={`Caroster ${event.name}`}
275              sx={{width: '100%', mt: 2}}
276            />
277          )}
278        </Card>
279      </Container>
280    </Box>
281  );
282};
283
284export const getServerSideProps = pageUtils.getServerSideProps(
285  async (context, apolloClient) => {
286    const {uuid} = context.query;
287    const {host = ''} = context.req.headers;
288    let event = null;
289
290    // Fetch event
291    try {
292      const {data} = await apolloClient.query({
293        query: EventByUuidDocument,
294        variables: {uuid},
295      });
296      event = data?.eventByUUID?.data;
297    } catch (error) {
298      return {
299        notFound: true,
300      };
301    }
302
303    return {
304      props: {
305        eventUUID: uuid,
306        metas: {
307          title: event?.attributes?.name || '',
308          url: `https://${host}${context.resolvedUrl}`,
309        },
310      },
311    };
312  }
313);
314export default Page;